package com.rjj.wlqq.tool.mail;

import com.jfinal.log.Log;
import com.sun.mail.util.MailSSLSocketFactory;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 ** 邮件
 *  * 1.普通文本 sendText  html消息  sendHtml
 *  * 2.图片
 *  * 3.附件 sendFile
 *  * 4.文本+图片 sendTextPhoto
 *  * 5.图片文本附件 sendTextPhotoFile
 *  * 6.抄送，密送
 *  * 7.群发【普通发送的时候把发送地址拼接字符串a,b.c】
 */
public class MailUtilMe {

    private Log logger = Log.getLog(MailUtilMe.class);

    //发件箱
    private String fromEmail = "215060580@qq.com";
    //发件箱别名
    private String personel = "淡淡人生过";
    //发件箱密码
    private String password = "rylihmxcanazcajb";
    //端口
    private String port = "465";
    //主机地址
    private String host = "smtp.qq.com";
    //收件人邮箱
    private String toEmail = "253481340@qq.com";
    //主题
    private String subject ="淡淡人生过的第一份邮件";

    //测试图片
    private static String pathName = "/Users/renjianjun/study/ideaWorkSpace/wlqq/src/main/resources/static/img/mao.jpg";


    //获取基本配置,这种可能会报错

    /**
     * 会报错javax.net.ssl.SSLHandshakeException:
     * sun.security.validator.ValidatorException:
     * PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
     * unable to find valid certification path to requested target
     *
     * 错误很简单，就是证书路径无效，网上有人说使用代码下载证书到本地jre证书库中，
     * 或者直接导入此证书到jre证书；确实可以；但是我们可以在代码中信任此站点证书
     * @return
     */
    public Properties getProperties(){
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
        Properties props = System.getProperties();
        props.setProperty("mail.host", host);//主机
        props.put("mail.transport.protocol", "smtp");//链接协议
        props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
        props.setProperty("mail.smtp.socketFactory.fallback", "false");
        props.setProperty("mail.smtp.port", port);
        props.setProperty("mail.smtp.socketFactory.port", port);
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable","true");//开启ssl加密
        props.put("mail.debug", "true");// 设置是否显示debug信息 true 会在控制台显示相关信息
        return props;
    }

    /**
     * 基本不会出现问题
     * @return
     */
    public Properties getProp(){

        Properties props = System.getProperties();
        props.setProperty("mail.host", host);//主机地址
        props.put("mail.transport.protocol", "smtp");//链接协议
        props.setProperty("mail.smtp.port", port);//端口
        props.put("mail.smtp.auth", "true"); // 允许smtp校验
        MailSSLSocketFactory sf = null;
        try {
            sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
        } catch (GeneralSecurityException e) {
            logger.info("error", e);
        }

        props.put("mail.smtp.ssl.enable", "true");//开启ssl加密
        props.put("mail.smtp.ssl.socketFactory", sf);
        props.put("mail.smtp.socketFactory.fallback", "false");
        props.put("mail.smtp.socketFactory.port",  port);
        props.put("mail.debug", "true");// 设置是否显示debug信息 true 会在控制台显示相关信息
        return props;
    }

    /**
     * 获取session
     */
    private Session getSession(){
        Properties prop = getProp();
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(fromEmail,password);
            }
        });

        return session;
    }


    private MimeMessage getMineMessage() {
        Session session = getSession();

        // 创建默认的 MimeMessage 对象
        MimeMessage message = new MimeMessage(session);

        try {

            // Set From: 头部头字段，设置了默认名称，就会显示发送人为 "淡淡人生过"
            message.setFrom(new InternetAddress(fromEmail,personel));

            /*
             * Set To: 头部头字段
             *
             * Message.RecipientType.TO  普通发送
             * Message.RecipientType.CC  抄送
             * Message.RecipientType.BCC 密送
             */
            //给一个人发送邮件
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
            //上边和下边这个一样
            //message.addRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail,false));//false参数暂时不知道什么意思，好像是一种校验的严格与否

            //给多个人发送邮件
            //message.addRecipients(Message.RecipientType.TO,new Address[]{new InternetAddress(toEmail),new InternetAddress(toEmail)});
            //message.addRecipients(Message.RecipientType.CC,new Address[]{new InternetAddress(toEmail)});
            //message.addRecipients(Message.RecipientType.BCC,new Address[]{new InternetAddress(toEmail)});

            // Set Subject: 头部头字段
            message.setSubject(subject,"UTF-8");

            //设置邮件的发送时间
            message.setSentDate(new Date());

        } catch (MessagingException e) {
            logger.info("1出现异常：" + e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.info("2出现异常：" + e.getMessage());
        }

        return message;
    }

    /**
     * 发送邮件
     *
     * 普通文本消息
     */
    public void sendText(String content) {
        MimeMessage message = getMineMessage();
        // 设置消息体
        try {
            message.setText(content);
            Transport.send(message);
        } catch (MessagingException e) {
            logger.info("设置文本内容出现异常：" + e.getMessage());
        }
    }

    /**
     * 发送邮件
     *
     * html消息
     */
    public void sendHtml(String content){
        MimeMessage message = getMineMessage();
        try {
            message.setContent(content,"text/html;charset=UTF-8");
            Transport.send(message);
        } catch (MessagingException e) {
            logger.info("设置html内容出现异常：" + e.getMessage());
        }
    }

    /**
     * 发送带附件的邮件
     */
    public void sendFile(String content, String pathFileName){
        MimeMessage message = getMineMessage();

        // 创建多重消息
        Multipart multipart = new MimeMultipart();

        try {

            // 创建消息部分
            BodyPart messageBodyPart = new MimeBodyPart();
            // 消息,这里的消息不发送也是可以的
           // messageBodyPart.setText(content);
            messageBodyPart.setContent(content, "text/html; charset=utf-8");

            // 设置文本消息部分
            multipart.addBodyPart(messageBodyPart);

            // 附件部分
            messageBodyPart = new MimeBodyPart();
            //获取文件
            DataSource source = new FileDataSource(pathFileName);
            messageBodyPart.setDataHandler(new DataHandler(source));
            //设置文件名称
            messageBodyPart.setFileName(MimeUtility.encodeText(source.getName()));
            //添加到多重消息容器中
            multipart.addBodyPart(messageBodyPart);

            // 发送完整消息
            message.setContent(multipart);

            //   发送消息
            Transport.send(message);
        } catch (MessagingException e) {
            logger.info("发送附件邮件出现异常：" + e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.info("文件名称编码异常：" + e.getMessage());
        }
    }

    /**
     * 发送多个文件
     * @param content 信息
     * @param pathFileName 多个附件的路径
     */
    public void sendFileList(String content, List<String> pathFileName){
        MimeMessage message = getMineMessage();

        try {
            // 创建多重消息
            Multipart multipart = new MimeMultipart();

            //消息
            BodyPart messageBodyPart = new MimeBodyPart();
            messageBodyPart.setContent(content,"text/html; charset=utf-8");
            multipart.addBodyPart(messageBodyPart);

            for (String filePath : pathFileName) {
                // 附件部分
                // 创建消息部分
                 messageBodyPart = new MimeBodyPart();
                //获取文件
                DataSource source = new FileDataSource(filePath);
                messageBodyPart.setDataHandler(new DataHandler(source));
                //设置文件名称
                messageBodyPart.setFileName(MimeUtility.encodeText(source.getName()));

                //添加到多重消息容器中
                multipart.addBodyPart(messageBodyPart);
            }
            // 发送完整消息
            message.setContent(multipart);

            //   发送消息
            Transport.send(message);
        } catch (MessagingException e) {
            logger.info("发送附件邮件出现异常：" + e.getMessage());
        } catch (UnsupportedEncodingException e) {
            logger.info("文件名称编码异常：" + e.getMessage());
        }
    }

    /**
     * 发送图片和附件邮件
     */
    public void sendTextPhotoFile(){
        MimeMessage message = getMineMessage();

        try {
            // 1.创建图片"节点"
            MimeBodyPart image = new MimeBodyPart();
            // 读取本地文件
            DataHandler dh = new DataHandler(new FileDataSource(pathName));
            // 将图片数据添加到"节点"
            image.setDataHandler(dh);
            // 为"节点"设置一个唯一编号（在文本"节点"将引用该ID）
            image.setContentID("mailTestPic");

            // 2. 创建文本"节点"
            MimeBodyPart text = new MimeBodyPart();
            // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片
            /**
             *
             这里在img标签中src属性中添加了cid：mailTestPic  其实就是把图片和我发送的图片关联起来了，点击图片就会跳到我的路径所在的地址
             这里使用a标签包裹了，我的图片，发送的内容其实时html的，所以有些标签不显示，其实一html的标签
             */
            text.setContent(
                    "这是一张图片<br/><a href='http://www.cnblogs.com/ysocean/p/7666061.html'>" + //我想要点击图片跳到的地址
                            "<img src='cid:mailTestPic'/>" + //图片，可以写网络图片的地址
                            "</a>"
                    , "text/html;charset=UTF-8");//html，使用utf-8,识别

            // 3. （文本+图片）设置 文本 和 图片"节点"的关系（将 文本 和 图片"节点"合成一个混合"节点"）
            MimeMultipart mm_text_image = new MimeMultipart();
            mm_text_image.addBodyPart(text);
            mm_text_image.addBodyPart(image);
            mm_text_image.setSubType("related");    // 关联关系

            // 4. 将 文本+图片 的混合"节点"封装成一个普通"节点"
            // 最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart,
            // 上面的 mailTestPic 并非 BodyPart, 所有要把 mm_text_image 封装成一个 BodyPart
            MimeBodyPart text_image = new MimeBodyPart();
            text_image.setContent(mm_text_image);

            // 5. 创建附件"节点"
            MimeBodyPart attachment = new MimeBodyPart();
            // 读取本地文件
            DataHandler dh2 = new DataHandler(new FileDataSource(pathName));
            // 将附件数据添加到"节点"
            attachment.setDataHandler(dh2);
            // 设置附件的文件名（需要编码）
            attachment.setFileName(MimeUtility.encodeText(dh2.getName()));

            // 6. 设置（文本+图片）和 附件 的关系（合成一个大的混合"节点" / Multipart ）
            MimeMultipart mm = new MimeMultipart();
            mm.addBodyPart(text_image);
            mm.addBodyPart(attachment);     // 如果有多个附件，可以创建多个多次添加
            mm.setSubType("mixed");         // 混合关系

            // 7. 设置整个邮件的关系（将最终的混合"节点"作为邮件的内容添加到邮件对象）
            message.setContent(mm);
            Transport.send(message);

        } catch (MessagingException e) {
            logger.info("图片文本文件消息异常：" + e.getMessage());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }
    public void sendTextPhoto(){
        MimeMessage message = getMineMessage();

        try {
            // 1.创建图片"节点"
            MimeBodyPart image = new MimeBodyPart();
            // 读取本地文件
            DataHandler dh = new DataHandler(new FileDataSource(pathName));
            // 将图片数据添加到"节点"
            image.setDataHandler(dh);
            // 为"节点"设置一个唯一编号（在文本"节点"将引用该ID）
            image.setContentID("mailTestPic");

            // 2. 创建文本"节点"
            MimeBodyPart text = new MimeBodyPart();
            // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片
            /*
             *
             这里在img标签中src属性中添加了cid：mailTestPic  其实就是把图片和我发送的图片关联起来了，点击图片就会跳到我的路径所在的地址
             这里使用a标签包裹了，我的图片，发送的内容其实时html的，所以有些标签不显示，其实一html的标签
             */
            text.setContent(
                    "这是一张图片<br/><a href='http://www.cnblogs.com/ysocean/p/7666061.html'>" + //我想要点击图片跳到的地址
                            "<img src='cid:mailTestPic'/>" + //图片，可以写网络图片的地址
                            "</a>"
                    , "text/html;charset=UTF-8");//html，使用utf-8,识别

            // 3. （文本+图片）设置 文本 和 图片"节点"的关系（将 文本 和 图片"节点"合成一个混合"节点"）
            MimeMultipart mm_text_image = new MimeMultipart();
            mm_text_image.addBodyPart(text);
            mm_text_image.addBodyPart(image);
            /*
             * 邮件体包含邮件的内容，它的类型由邮件头的“Content-Type”域指出。常见的简单类型有text/plain(纯文本)和text/html(超文本)。有时也会出现的multipart类型，是MIME邮件的精髓。邮件体被分为多个段，每个段又包含段头和段体两部分，这两部分之间也以空行分隔。常见的multipart类型有三种：multipart/mixed, multipart/related和multipart/alternative。
             *
             * multipart/mixed：附件。
             *
             * multipart/related：内嵌资源。
             *
             * multipart/alternative：纯文本与超文本共存。
             */
            mm_text_image.setSubType("mixed");    // 关联关系,这里图片相当于附件了，只是在正文内容中一img标签显示出来了

            // 7. 设置整个邮件的关系（将最终的混合"节点"作为邮件的内容添加到邮件对象）
            message.setContent(mm_text_image);
            Transport.send(message);

        } catch (MessagingException e) {
            logger.info("发送文本图片消息出现异常：" + e.getMessage());
        }

    }

    public static void main(String[] args) {

        MailUtilMe m = new MailUtilMe();
        //m.sendTextPhotoFile();
        //m.sendText("a");
        //m.sendHtml("<b>你好</b>");
        // m.sendFile("刘亦菲哦",pathName);
       // m.sendTextPhoto();

        List<String> list = new ArrayList<>(2);
        list.add(pathName);
        list.add(pathName);
        m.sendFileList("猫猫",list);
    }
}
